1. Enums
A modo de ejemplo práctico, veamos una definición de errores que quisiéramos definir para nuestra aplicación. En JavaScript, podríamos hacer algo así:
En TS, sin embargo, tenemos las enumeraciones o enums. Los enums, se pueden utilizar para colecciones de datos finitas (días de la semana, meses del año,...) pero nunca para colecciones de datos infinitas.
Fijémonos en que los enums, funcionan de la misma manera que los types, en cuanto a que al pasarlos como parámetro a una función hay que especificar el tipo de dato que estamos pasando.
2. Sobre las aserciones de tipos
Hemos venido hablando de que la inferencia de tipos identifica los métodos y propiedades del objeto perteneciente al tipo de dato en cuestión, dentro de TypeScript (array -> splice, string -> length ... ). JS trabaja con la API del DOM, en la cual los objetos correspondientes a los elementos HTML renderizados, tienen sus propias propiedades y métodos.
Lo que pasa es que TS no funciona en tiempo de ejecución, como si lo hacer JS, lo cual hace que la inferencia de datos no pueda vincularse con la API del DOM, donde se referencian todos los elementos HTML.
Lo que se suele hacer aquí es una aserción de tipos. En el código que se muestra, le estamos diciendo que identifique el valor de la constante canvas como un HTMLCanvasElement.
No obstante, a TS no le estamos dando una garantía real de que la constante canvas vaya a ser un elemento canvas, y si no lo fuese, el JS resultante fallaría en tiempo de ejecución.
Hay que ser precavido con las aserciones de código, y por ello lo recomendable es hacer al menos este tipo de comprobación:
No obstante, también nos podría llegar a fallar si referenciase un elemenento span, por ejemplo, en lugar de uno canvas. Lo ideal, sería hacer esta comprobación. En realidad, la compilación que nos devolverá JavaScript será el mismo código, pero estaremos asegurándonos de que no se produzcan errores, ya que el objetivo de trabajar con TS, es ese. En este caso no estaríamos utilizando la aserción, pero seríamos más correctos en lo relativo a la filosofía de TS.
En este caso, respetamos la inferencia, ya que TS está deduciendo que canvas es un HTMLCanvas.
El operador InstanceOf pertenece a JS: Referencia documentación Mozilla. Si recordamos, JS tiene un operador primo-hermano llamado TypeOf.
3. Fetching de datos en TypeScript
Si intentamos hacer un fetch en TS, ya de buenas a primeras, obtendremos este error.
Nos pide que transformemos el archivo en un módulo. De manera oficial, deberíamos transformar el fichero a una extensión .mts, y así poder utilizar los módulos, pero frameworks y entornos como Vite.JS nos hacen la magia sin que nos enteremos.
El problema es que TS no es capaz de inferir datos en data, ya que por defecto le va asignar un tipo any.
Solventar el tipado de los datos que vamos a sacar de la respuesta de la API, supondría definir los valores de la API como tal en nuestro código, lo cual puede ser sumamente tedioso.
Afortunadamente, existen extensiones VSCODE y herramientas online como QuickType que nos permiten obtener el mapeo de datos que hacen las APIS de manera rápida.
-
Hacemos la petición en el navegador
-
Copiamos la respuesta.
-
Vamos a QuickType, pegamos la respuesta, especificamos un nombre en la parte superior y configuramos las opciones que consideremos.
-
Copiamos y pegamos en nuestro código la respuesta que nos da la herramienta.
Aquí utilizar la aserción de tipos si sería correcto.
Ahora la inferencia de datos funciona correctamente, de tal manera que si utilizamos datos que existen, TS nos da el visto bueno, pero si le pasamos algo que no esté definido en la API, se nos quejará.
Pero claro, ni si quiera esto nos daría una garantía absoluta de validación en caso de que la API (por una actualización, o por el motivo que fuese) devolviera otra cosa. Para eso, la herramienta quicktype nos permite utilizar la librería Zod que valida los datos en tiempo real. La operativa por lo demás, sería la misma.
3. Interfaces
Al igual que los tipos, las interfaces son otra manera de definir el "contrato" en el que se especifica la estructura que deben seguir los objetos. Son prácticamente iguales, pero con ligeras diferencias.
Las interfaces se pueden anidar.
Podemos extender interfaces:
El operador de unión también lo podemos ver con las interfaces.
Declarar funciones en interfaces
Tenemos estas dos formas de declarar funciones en una interfaz:
Dentro de las funciones, otra diferencia con los types, tiene que ver con este error.
En este caso, nos dirá que la interfaz tiene identificadores duplicados, ya que tiene métodos con nombres idénticos. Sin embargo, TS nos permite separar los métodos en dos bloques. Lo malo, es que puede dar la sensación de estar haciendo una especie de doble declaración, y hay que acostumbrarse. Podemos pensar que es como si hiciese un extend de manera automática.
Con los types, esto no pasa. No puedes utilizar el nombre del tipo más de una vez
Types vs interfaces
Un poco al hilo de lo que estamos comentando, podemos consultar el siguiente enlace de la documentación oficial:
Diferencias entre tipos e interfaces
4. Narrowing
El narrowing es una forma de ir desaciéndote, o filtrando los tipos que no quieras usar en un punto del código, si bien previamente un operador de propiedades opcionales.
Narrowing en propiedades
Si en este caso pedimos el length, TS se nos queja porque le hemos dicho, mediante una propiedad opcional, que el valor del objeto puede ser un number, y length no existe en number.
En este caso en particular, la solución es fácil:
Narrowing en métodos
En los métodos, pasa un poco lo mismo. Aunque utilicemos propiedades opcionales en una constante (Personaje puede ser, o Mario o Sonic), TS se nos quejará si tratamos de llamar a un método que no tengan ambos interfaces
Una de las soluciones en este caso, pasaría por tener un key que siempre fuese único.
De esta manera, podríamos hacer la comparativa. A esto se le suele llamar "propiedades discriminantes".
Habría una segunda forma en la que podríamos activar la inferencia sin depender de la propiedad dircriminante. Tendríamos que sacar la validación a una función que usa la palabra reservada is, y valida que el método que queremos utilizar no sea undefined. A este tipo de validaciones se le suele llamar type guard.
5. Clases: Instance of y Class
Pendiente